it looks like translatable strings, such as https://github.com/roots/sage/blob/bea1b34322d38c04a16f373170154051244c4982/resources/views/index.blade.php#L8, aren't been picked up by translation tools
if you swap {{ }} with <?php ?> then the strings are detected
- {{ __('Sorry, no results were found.', 'sage') }}
+ <?php __('Sorry, no results were found.', 'sage'); ?>
discourse thread: https://discourse.roots.io/t/localization-of-blade-templates/9331
tools:
makepot.php)For PoEdit (1.x and 2.x) following this guide should work.
http://framework.themosis.com/docs/master/blade/
I do not think this issue should be resolved on Sage level but rather on the translation tools themselves.
Thanks, but PoEdit still can't find any string if not within a <?php ?> block:
php
// wp-content/themes/sage/templates/test.blade.php
<?php __('test'); ?> // works
{{__('test2')}} // doesn't work
@php(_e('test3')) // doesn't work
I also tried these tools:
without luck, the generated .pot files are always empty. Too bad because it could be great to have a command to generate .pot files, like yarn run localize.
Just stumbled upon a PHP solution, https://github.com/xinax/laravel-gettext. Did anyone tried to integrate something like this?
By the way, looks like the WPML team doesn't plan to do anything more to support Blade templates: https://wpml.org/forums/topic/string-translation-in-blade-template-engine/
Thanks.
@nicooprat I'm using PoEdit, WPML and Sage 9 Bladeand following the guide above it works.
EDIT: Have you added the keywords?
_e
_x
esc_html__
esc_html_e
_n
_nx
_n_noop
_nx_noop
_ex
Thanks for your feedback. Here's my config:




(Btw, I agree that's not sth to be managed by Sage theme, but its tooling, either with PHP or JS)
I'm using the exact same config and it works.
I only have a slight difference, I'm using the theme root folder and I've added exclusions for vendor/node_modules/(...)
Currently I'm on PoEdit 2.0, but the same config worked on previous versions.
Try removing the *.scout.php, leave only *.blade.php
Unfortunately removing *.scout.php didn't solve it.
Also tried the configuration you recommend, no change.
I just did it all over again, by creating a fresh new .po file in the theme root directory with PoEdit, no luck either. :(
Do we agree that the goal is to avoid using <?php ?> blocks, like in my screenshot above?
When you say that it's working with WPML, do you mean that you scan the theme with PoEdit, leave translations empty, and then fill them with the WPML String Translation plugin?
Thanks for your time.
I'm using Poedit 2 (macOS) with the Blade extractor settings in a Multilingual Press (multisite) setup. Poedit works like a charm with the {{ __('String','namespace') }} definitions. I've created a .pot file first.
Make sure you include the load textdomain method and define your lang folder.
Update:
This works:
<h1>{{ __('Always free','sage') }}</h1>
But attributes don't seem to work:
<div data-readmore="{{ __('Show more', 'sage') }}">
It seems I'm only able to make this work for either php or blade, not both at once.
Is it possible to ship a config file for PoEdit bundled with Sage?
I think it works well with xgettext on the command line using this in the theme folder (replace \$ find ./resources ./app -iname "*.php" | xargs xgettext --language=Python --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=<textdomain> -k__ -k_e -k_n -k_x -k_ex -k_nx -kesc_attr__ -kesc_attr_e -kesc_attr_x -kesc_html__ -kesc_html_e -kesc_html_x -k_n_noop -k_nx_noop -o resources/languages/<theme>.pot
Ok this one seems to work! Hooray :) So here's the workflow:
__('string', 'domain')THEME_FOLDER/lang (they're easily found by Wordpress here). lang folder with the correct name syntax, eg. fr_FR or en_US.If you're using WPML, in the "Theme & plugin localization" tab, you should see the table for each lang with the corresponding "The file exists" in front of it:

You can also add the terminal command to your package.json file and then run yarn run pot:
json
{
"name": "sage",
...
"scripts": {
...
"pot": "find ./templates -iname \"*.php\" | xargs xgettext --language=Python --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=<domain> -k__ -k_e -k_n -k_x -k_ex -k_nx -kesc_attr__ -kesc_attr_e -kesc_attr_x -kesc_html__ -kesc_html_e -kesc_html_x -k_n_noop -k_nx_noop -o ./lang/<domain>.pot"
},
}
Many thanks.
Closing this since it's now a documentation issue.
I believe this is still an issue as it won't pick __() between quotes (in HTML attributes)
So @lekkerduidelijk comment is still valid from what i tested.
See https://stackoverflow.com/a/34249157/632487
So best solution seems to compile blade templates and run xgettext on it.
To do this i guess https://github.com/alwaysblank/blade-generate would help!
Thanks @nicooprat麓s! I've modified the terminal command to match the current directory structure and parsing .php and .blade.php in separate commands, since it gave me warnings on php files:
{
"name": "sage",
...
"scripts": {
...
"pot": "mkdir -p ./resources/lang && find ./resources ./app -iname \"*.php\" | xargs xgettext --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=de_DE -k__ -k_e -k_n:1,2 -k_x:1,2c -k_ex:1,2c -k_nx:4c,12 -kesc_attr__ -kesc_attr_e -kesc_attr_x:1,2c -kesc_html__ -kesc_html_e -kesc_html_x:1,2c -k_n_noop:1,2 -k_nx_noop:3c,1,2, -k__ngettext_noop:1,2 -o resources/lang/sage.pot && find ./resources -iname '*.blade.php' | xargs xgettext --language=Python --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=de_DE -k__ -k_e -k_n:1,2 -k_x:1,2c -k_ex:1,2c -k_nx:4c,12 -kesc_attr__ -kesc_attr_e -kesc_attr_x:1,2c -kesc_html__ -kesc_html_e -kesc_html_x:1,2c -k_n_noop:1,2 -k_nx_noop:3c,1,2, -k__ngettext_noop:1,2 -j -o resources/lang/sage.pot"
},
}
I was getting an error when the pot file wasn't exist. Fixed by adding
touch ./resources/lang/sage.pot
@dsturm I cannot seem to make your command work.
Am I missing anything?
@apintocr Hmmm - do you get any error message? What system are you using? Any files created?
@dsturm I'm using Windows and Ubuntu. Obviously it is not working on Windows ;)
On ubuntu, it returns:
xgettext: no input file given
But the file is there.
Hi,
For all Polylang (Pro) users: I am using this plugin for theme translation: https://wordpress.org/plugins/theme-translation-for-polylang/
I've used it before for Timber/Twig projects and now for the first time for a Sage/Laravel project. You can use {{ pll__('String to translate') }} (note the missing text domain, the plugin will add it for you) and your strings will automatically appear in Languages > String translation. No need to worry about .po, .mo or .pot files.
Edit:
I just noticed this plugin will only pick up string from /resources, not from /app. I'm looking into a solution for that.
Edit 2:
This might not be the prettiest way to do it, but if it works it ain't stupid:
if (class_exists('Polylang_Theme_Translation')) {
class Sage_Polylang_Theme_Translation extends Polylang_Theme_Translation
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->run_sage_theme_scanner();
}
/**
* Find strings in /theme/app folder.
*/
protected function run_sage_theme_scanner()
{
$themes = wp_get_themes();
if (!empty($themes)) {
foreach ($themes as $name => $theme) {
if ($name === 'YOUR_THEME_NAME/resources') {
$theme_path = $theme->theme_root . DIRECTORY_SEPARATOR . $name;
$app_path = str_replace('resources', 'app', $theme_path);
$app_controllers_path = "{$app_path}/controllers";
$app_files = $this->get_files_from_dir($app_path);
$app_controllers_files = $this->get_files_from_dir($app_controllers_path);
$app_strings = $this->file_scanner($app_files);
$app_controllers_strings = $this->file_scanner($app_controllers_files);
$strings = array_merge($app_strings, $app_controllers_strings);
$this->add_to_polylang_register($strings, $name);
}
}
}
}
}
new Sage_Polylang_Theme_Translation();
}
for anyone coming here in the future: https://roots.io/sage/docs/theme-localization/
for updates/improvements, please submit a PR to the docs or open a discussion on roots discourse
@dsturm @apintocr
Got an error too:
$ yarn pot
yarn run v1.5.1
$ mkdir -p ./resources/lang && find ./resources ./app -iname "*.php" | xargs xgettext --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=de_DE -k__ -k_e -k_n:1,2 -k_x:1,2c -k_ex:1,2c -k_nx:4c,12 -kesc_attr__ -kesc_attr_e -kesc_attr_x:1,2c -kesc_html__ -kesc_html_e -kesc_html_x:1,2c -k_n_noop:1,2 -k_nx_noop:3c,1,2, -k__ngettext_noop:1,2 -o resources/lang/sage.pot && find ./resources -iname '*.blade.php' | xargs xgettext --language=Python --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=de_DE -k__ -k_e -k_n:1,2 -k_x:1,2c -k_ex:1,2c -k_nx:4c,12 -kesc_attr__ -kesc_attr_e -kesc_attr_x:1,2c -kesc_html__ -kesc_html_e -kesc_html_x:1,2c -k_n_noop:1,2 -k_nx_noop:3c,1,2, -k__ngettext_noop:1,2 -j -o resources/lang/sage.pot
xargs: xgettext: No such file or directory
error An unexpected error occurred: "Command failed.
Exit code: 127
Command: sh
Arguments: -c mkdir -p ./resources/lang && find ./resources ./app -iname \"*.php\" | xargs xgettext --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=de_DE -k__ -k_e -k_n:1,2 -k_x:1,2c -k_ex:1,2c -k_nx:4c,12 -kesc_attr__ -kesc_attr_e -kesc_attr_x:1,2c -kesc_html__ -kesc_html_e -kesc_html_x:1,2c -k_n_noop:1,2 -k_nx_noop:3c,1,2, -k__ngettext_noop:1,2 -o resources/lang/sage.pot && find ./resources -iname '*.blade.php' | xargs xgettext --language=Python --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=de_DE -k__ -k_e -k_n:1,2 -k_x:1,2c -k_ex:1,2c -k_nx:4c,12 -kesc_attr__ -kesc_attr_e -kesc_attr_x:1,2c -kesc_html__ -kesc_html_e -kesc_html_x:1,2c -k_n_noop:1,2 -k_nx_noop:3c,1,2, -k__ngettext_noop:1,2 -j -o resources/lang/sage.pot
Directory: /Users/sandro/Sites/Hostpoint/madbox.ch/web/app/themes/madbox
Output:
".
info If you think this is a bug, please open a bug report with the information provided in "/Users/sandro/Sites/Hostpoint/madbox.ch/web/app/themes/madbox/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
@retlehs
In your docs, the Part
... app -iname "*.php" | xargs ...
should be
... app -iname \"*.php\" | xargs ...
right? But as i wrote in the comment before, for me it doesn't work.
please read the error message you provided:
xargs: xgettext: No such file or directory
i'm locking this because we prefer to use roots discourse for conversations & support. every reply on here notifies everyone who is watching the repo
Most helpful comment
Ok this one seems to work! Hooray :) So here's the workflow:
__('string', 'domain')THEME_FOLDER/lang(they're easily found by Wordpress here).langfolder with the correct name syntax, eg.fr_FRoren_US.If you're using WPML, in the "Theme & plugin localization" tab, you should see the table for each lang with the corresponding "The file exists" in front of it:
You can also add the terminal command to your package.json file and then run
yarn run pot:json { "name": "sage", ... "scripts": { ... "pot": "find ./templates -iname \"*.php\" | xargs xgettext --language=Python --add-comments=TRANSLATORS --force-po --from-code=UTF-8 --default-domain=<domain> -k__ -k_e -k_n -k_x -k_ex -k_nx -kesc_attr__ -kesc_attr_e -kesc_attr_x -kesc_html__ -kesc_html_e -kesc_html_x -k_n_noop -k_nx_noop -o ./lang/<domain>.pot" }, }Many thanks.