Reputation: 397
The question as above, after translation server does its work, some languages have much longer strings than others.
I'm aware that containers might resize, or that I can clip text, or wrap it. But what if I have set containers size and do not want to change it, and wrappping text does not help?
My idea is to resize text, but I don't know what should I do after TranslationServer.set_locale(lang)
in order to do so?
My best bet was, to set all labels/buttons clip_text
properity to true
and do such thing:
TranslationServer.set_locale(lang)
loop through entire tree:
if CURRENT_NODE does have properity .text:
var clipping = true
while clipping
if CURRENT_NODE clip_text properity is currently NOT clipping anything #I have no idea how to check this?
clipping = false
else:
CURRENT_NODE font_size -= 1
My next idea was to turn clip_text off and loop through every node like above, but instead of checking clipping activity, making an invisible for user copy of the node that does copy node width size as well, but this time allow the node to resize, and measure the node width, compare to original, and resize font like above until the copied node will be the same or lower width, and then set established int his way fint size to original node. But this is crazy overkill, and seems like a weird thing to do, right? It need to be done every time for every new node that contains text, and if any text does change over and over again. Is this how it suppose to look like?
Oversized strings after translations seems like a common problem, right? But I'm unable to find any reasonable solutions or examples that might help me in my case. Any help would be great.
EDIT:
@Theraot Thank You for such great answer! The main idea that You are suggesting is, that I should design game for the longest string possible and work arund it. I do agree, however:
text_overrun_behaviour
and set it to Trim Characters
, and then check how many characters are actually displayed. This would be perfect way to achieve such simple thing, but I can't find any way to check the label text actual size, and I'm really surprised, that this is not a simple task.$label.text
it does return the placeholder for translation server, and not the actual text that is inside the label. I understand why this is the case, however in order to get current text I need to use tr($label.text)
- and this is also "fine", but I obviously do not get actual displayed (and trimed, or simply changed via dynamic %s
) text. Am I missing some obvious solution on how to get the label text that user does see, and not the placeholder?Unicode parsing error, some characters were replaced with � (U+FFFD): NUL character
=> A LOT of this, and finally: [output overflow, print less text!]
. It does happen even if I tested it with print("test")
. What is going on here? I am using font that does have normal alphabet and we are talking about languages that uses "normal" characters.Upvotes: 2
Views: 424
Reputation: 40315
First advice is to design and test with long text. Which you can approach with Pseudolocalization.
You seem to want to scale down (making the text smaller, be aware of stretch mode in project settings), which might lead to the font being ilegible.
With that out of the way...
I'd suggest to set autowrap_mode
to AUTOWRAP_OFF
or AUTOWRAP_WORD_SMART
in Label
and RichTextLabel
.
And set fit_content
to true in RichTextLabel
. So they resize with the text.
If use visible_characters
, set visible_characters_behavior
to VC_CHARS_AFTER_SHAPING
.
Yes, this brings issues with aligment (e.g the Control
resizes and it is no longer centered), you can react to the resize (or both theme and locale changes) and use set_anchors_and_offsets_preset
.
I have this in my labels:
func _notification(what: int) -> void:
if what == NOTIFICATION_TRANSLATION_CHANGED or what == NOTIFICATION_THEME_CHANGED:
_update_text()
Where _update_text()
does whathever I need to do. I use a deferred call to execute set_anchors_and_offsets_preset
.
You can use a similar approach for OptionButton
.
OK, that is labels, but other controls might need to resize, or do not accomodate the customization you want.
For example, if the default behavior of Button
is not good, you can create a custom Button
class that instantiates a Label
inside. The Label
works as described above. And the Button
resizes itself to the size of the Label
(plus some extra margin if you want). You can do this in _process
or in the resized
signal of the Label
The same idea can work for a Container
that resizes to match the size of the children.
A more general solution would be to have a Control
that copies the size and position of another Control
, and add an extra margin. I can give the code in full for that:
@tool
extends Control
@export var source:Control:
set(mod_value):
if source == mod_value:
return
source = mod_value
if is_node_ready():
_update_source()
@export var grow_left:float
@export var grow_top:float
@export var grow_right:float
@export var grow_bottom:float
func _ready() -> void:
_update_source()
func _exit_tree() -> void:
if is_queued_for_deletion():
return
request_ready()
func _process(_delta: float) -> void:
if not is_instance_valid(source):
_update_source()
return
var parent_control := get_parent_control()
# global transform of the parent
var parent_control_global_transform := Transform2D.IDENTITY
if is_instance_valid(parent_control):
parent_control_global_transform = parent_control.get_global_transform()
# global transform of source
var _source_global_transform := source.get_global_transform()
# transform of source relative to the parent
var _source_to_local_transform := parent_control_global_transform.affine_inverse() * _source_global_transform
visible = source.visible
size_flags_horizontal = source.size_flags_horizontal
size_flags_vertical = source.size_flags_vertical
size_flags_stretch_ratio = source.size_flags_stretch_ratio
custom_minimum_size = source.get_combined_minimum_size() + Vector2(grow_left + grow_right, grow_top + grow_bottom)
size = source.size + Vector2(grow_left + grow_right, grow_top + grow_bottom)
global_position = _source_global_transform.origin - Vector2(grow_left, grow_top)
rotation = _source_to_local_transform.get_rotation()
scale = _source_to_local_transform.get_scale()
func _update_source() -> void:
if not is_inside_tree():
return
if not is_instance_valid(source):
source = get_parent_control()
set_process(is_instance_valid(source))
You can think of this as a poor man's remote transform for Control
.
The above code uses _process
because not all changes have appropiate notifications.
So you can, for example, have it follow the size and position of a VBoxContainer
that has Button
s that might resize due to local changes... And then inside of it have Panel
with an StyleBox
that provides a border around the VBoxContainer
.
In some cases you might also resource to using HFlowContainer
and VFlowContainer
, so your Control
s can move to another row or column.
And I believe the last cases would be when you want to hide the overflow and provide a "see more" option or pagination, which I believe would be made bespoke case by case.
After writing the answer the idea that perhaps you didn't want to change the size of the elements because they are skewmorphic.
For exmaple, we might be talking about a playing card with some text on it, it makes not sense to change the size of the card to fit the text.
However, it remains true that making the text smaller can lead to the text not being legible.
I can think of a few compromises:
I would expect that skewmorphic are not everywhere on your design, and so you can have dedicated code to scale them, and so you do not have to iterate over every node as you say in the question.
On numberal 1: Yes you need to test all your languages. I don't know of a tool to make it easier, but even with a tool, testing is important.
On numeral 2 and 3: What comes to mind is to use a hidden Label
to measure the size without wrapping and compare it. There might be other ways to do it, such as drawing the text to a bitmap and measuring it there, but that seems too much trouble. Perhaps something on TextServer
is useful, but I'm not sure. Addendum from comments: This was solved with the get_string_size
method from the Font
class.
On numeral 4: I disable autotranslation, and instead have code that sets the translated text when I get NOTIFICATION_TRANSLATION_CHANGED
. This also allows me to process the translated text before assigning it (e.g. string format or regex replace).
On numeral 5: Idk why you would get unicode errors, that might be a bug. If you can make a minimun reproduction project, please report it at https://github.com/godotengine/godot/issues - About the overflow, you might be able to increase the output buffer size. It is in Project Settings -> Network -> Limits -> Debugger.
Upvotes: 2